package learn.up.java.thread.threads;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 如果说上面就是Lock，那么它不能成为同步问题更完美的处理方式，下面要介绍的是读写锁(ReadWriteLock)，
 * 我们会有一种需求，在对数据进行读写的时候，为了保证数据的一致性和完整性，需要读和写是互斥的，写和
 * 写是互斥的，但是读和读是不需要互斥的，这样读和读不互斥性能更高些
 * 
 * @author NANUTO
 * @date 2017年12月10日 下午1:09:22
 */
public class Lock2 {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newFixedThreadPool(5);

//		Data data = new Data();  //		没有实行同步
//		Data2 data = new Data2();  //使用 synchronized同步
		Data3 data = new Data3();  //使用读写锁,ReentrantReadWriteLock
		for (int i = 0; i < 3; i++) {
			pool.execute(new Runnable() {
				public void run() {
					for (int j = 0; j < 5; j++) {
						data.set(new Random().nextInt(30));
					}
				}
			});
		}
		for (int i = 0; i < 3; i++) {
			pool.execute(new Runnable() {
				public void run() {
					for (int j = 0; j < 5; j++) {
						data.get();
					}
				}
			});
		}
		pool.shutdown();
	}
}

/**
 * 这里是没有实现互斥的情形，读写同时并发执行，这样不能保证数据的一致性和完整性
 * 
 * @author NANUTO
 * @date 2017年12月10日 下午1:41:38
 */
class Data {
	// 共享数据
	private int data;

	public void set(int data) {
		System.out.println("ready read data");
		try {
			Thread.sleep(20);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.data = data;
		System.out.println(Thread.currentThread().getName() + " read data: " + this.data);
	}

	public void get() {
		System.out.println("ready write data");
		try {
			Thread.sleep(20);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " write data: " + this.data);
	}
}

/**
 * 第一种方式：使用synchronized，这种方式，写入和写入互斥，读取和写入互斥，读取和读取互斥 
 * 在各个方法上加上synchronized
 * 
 * @author NANUTO
 * @date 2017年12月10日 下午1:43:22
 */
class Data2 {
	// 共享数据
	private int data;

	public synchronized void set(int data) {
		System.out.println("ready read data");
		try {
			Thread.sleep(20);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.data = data;
		System.out.println(Thread.currentThread().getName() + " read data: " + this.data);
	}

	public synchronized void get() {
		System.out.println("ready write data");
		try {
			Thread.sleep(20);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " write data: " + this.data);
	}
}
/**
 *		我们发现，虽然写入和写入互斥了，读取和写入也互斥了，但是读取和读取之间也互斥了，
 *不能并发执行，效率较低，用读写锁实现代码如下：
 * @author NANUTO
 * @date 2017年12月10日 下午1:53:49
 */
class Data3 {
	//共享数据
	private int data;  
	
	private ReadWriteLock lock = new ReentrantReadWriteLock();
	
	public void set(int data) {
//		取到写锁，锁住
		Lock writeLock = lock.writeLock();
		
		writeLock.lock();
		try {
			System.out.println("ready write data");
			Thread.sleep(20);
			this.data = data;
			System.out.println(Thread.currentThread().getName() + " write data: " + this.data);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			// 释放写锁
			writeLock.unlock();
		}
		
	}

	public void get() {
		// 取到读锁
		Lock readLock = lock.readLock();
		readLock.lock();
		try {
			System.out.println("ready read data");
			Thread.sleep(20);
			System.out.println(Thread.currentThread().getName() + " read data: " + this.data);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			// 释放读锁  
			readLock.unlock();
		}
		
	}
}